package com.fengpb.friday.conductor.common.metadata.workflow;

import com.fengpb.friday.conductor.common.metadata.task.TaskDef;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.util.*;


public class WorkflowTask {

    @NotEmpty(message = "任务定义name不可为空")
    private String name;

    private String taskReferenceName;

    private String description;

    private Map<String, Object> inputParameters = new HashMap<>();

    private String type = TaskType.HTTP.name();

    private String caseValueParam;

    private String caseExpression;

    private Map<String,@Valid List<@Valid WorkflowTask>> decisionCases = new LinkedHashMap<>();

    private List<@Valid WorkflowTask> defaultCase = new LinkedList<>();

    private List<@Valid List<@Valid WorkflowTask>> forkTasks = new LinkedList<>();

    private List<WorkflowTask> loopOver = new LinkedList<>();

    private TaskDef taskDefinition;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getTaskReferenceName() {
        return taskReferenceName;
    }

    public void setTaskReferenceName(String taskReferenceName) {
        this.taskReferenceName = taskReferenceName;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Map<String, Object> getInputParameters() {
        return inputParameters;
    }

    public void setInputParameters(Map<String, Object> inputParameters) {
        this.inputParameters = inputParameters;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getCaseValueParam() {
        return caseValueParam;
    }

    public void setCaseValueParam(String caseValueParam) {
        this.caseValueParam = caseValueParam;
    }

    public String getCaseExpression() {
        return caseExpression;
    }

    public void setCaseExpression(String caseExpression) {
        this.caseExpression = caseExpression;
    }

    public Map<String, List<WorkflowTask>> getDecisionCases() {
        return decisionCases;
    }

    public void setDecisionCases(Map<String, List<WorkflowTask>> decisionCases) {
        this.decisionCases = decisionCases;
    }

    public List<WorkflowTask> getDefaultCase() {
        return defaultCase;
    }

    public void setDefaultCase(List<WorkflowTask> defaultCase) {
        this.defaultCase = defaultCase;
    }

    public List<List<WorkflowTask>> getForkTasks() {
        return forkTasks;
    }

    public void setForkTasks(List<List<WorkflowTask>> forkTasks) {
        this.forkTasks = forkTasks;
    }

    public List<WorkflowTask> getLoopOver() {
        return loopOver;
    }

    public void setLoopOver(List<WorkflowTask> loopOver) {
        this.loopOver = loopOver;
    }

    public TaskDef getTaskDefinition() {
        return taskDefinition;
    }

    public void setTaskDefinition(TaskDef taskDefinition) {
        this.taskDefinition = taskDefinition;
    }

    private Collection<List<WorkflowTask>> children() {
        Collection<List<WorkflowTask>> workflowTaskLists = new LinkedList<>();
        TaskType taskType = TaskType.valueOf(type);

        switch (taskType) {
            case DECISION:
                workflowTaskLists.addAll(decisionCases.values());
                workflowTaskLists.add(defaultCase);
                break;
            case FORK_JOIN:
                workflowTaskLists.addAll(forkTasks);
                break;
            case DO_WHILE:
                workflowTaskLists.add(loopOver);
                break;
            default:
                break;
        }
        return workflowTaskLists;
    }

    public WorkflowTask next(String taskReferenceName, WorkflowTask parent) {
        TaskType taskType = TaskType.valueOf(type);

        switch (taskType) {
            case DO_WHILE:
            case DECISION:
                for (List<WorkflowTask> workflowTasks : children()) {
                    Iterator<WorkflowTask> iterator = workflowTasks.iterator();
                    while (iterator.hasNext()) {
                        WorkflowTask task = iterator.next();
                        if (task.getTaskReferenceName().equals(taskReferenceName)) {
                            break;
                        }
                        WorkflowTask nextTask = task.next(taskReferenceName, this);
                        if (nextTask != null) {
                            return nextTask;
                        }
                        if (task.has(taskReferenceName)) {
                            break;
                        }
                    }
                    if (iterator.hasNext()) {
                        return iterator.next();
                    }
                }
                if (taskType == TaskType.DO_WHILE && this.has(taskReferenceName)) {
                    // come here means this is DO_WHILE task and `taskReferenceName` is the last task in
                    // this DO_WHILE task, because DO_WHILE task need to be executed to decide whether to
                    // schedule next iteration, so we just return the DO_WHILE task, and then ignore
                    // generating this task again in deciderService.getNextTask()
                    return this;
                }
                break;
            case FORK_JOIN:
                boolean found = false;
                for (List<WorkflowTask> workflowTasks : children()) {
                    Iterator<WorkflowTask> iterator = workflowTasks.iterator();
                    while (iterator.hasNext()) {
                        WorkflowTask task = iterator.next();
                        if (task.getTaskReferenceName().equals(taskReferenceName)) {
                            found = true;
                            break;
                        }
                        WorkflowTask nextTask = task.next(taskReferenceName, this);
                        if (nextTask != null) {
                            return nextTask;
                        }
                        if (task.has(taskReferenceName)) {
                            break;
                        }
                    }
                    if (iterator.hasNext()) {
                        return iterator.next();
                    }
                    if (found && parent != null) {
                        return parent.next(this.taskReferenceName, parent);        //we need to return join task... -- get my sibling from my parent..
                    }
                }
                break;
            case TERMINATE:
            default:
                break;
        }
        return null;
    }

    public boolean has(String taskReferenceName) {
        if (this.getTaskReferenceName().equals(taskReferenceName)) {
            return true;
        }
        TaskType taskType = TaskType.valueOf(type);

        switch (taskType) {
            case DECISION:
            case DO_WHILE:
            case FORK_JOIN:
                for (List<WorkflowTask> childx : children()) {
                    for (WorkflowTask child : childx) {
                        if (child.has(taskReferenceName)) {
                            return true;
                        }
                    }
                }
                break;
            default:
                break;
        }
        return false;
    }

    public List<WorkflowTask> collectTasks() {
        List<WorkflowTask> tasks = new LinkedList<>();
        tasks.add(this);
        for (List<WorkflowTask> workflowTaskList : children()) {
            for (WorkflowTask workflowTask : workflowTaskList) {
                tasks.addAll(workflowTask.collectTasks());
            }
        }
        return tasks;
    }
}
